use super::*;

/// A trait to parse item in [`RecordList`].
///
/// Internal use only.
pub trait RecordListItem<'a>: Sized {
	/// Parses raw data.
	fn parse(tag: Tag, data: &'a [u8]) -> Option<Self>;
}

/// A data storage used by [`ScriptList`], [`LanguageSystemList`] and [`FeatureList`] data types.
#[derive(Clone, Copy)]
pub struct RecordList<'a, T: RecordListItem<'a>> {
	pub data: &'a [u8],
	records: LazyArray16<'a, TagRecord>,
	data_type: core::marker::PhantomData<T>,
}
impl<'a, T: RecordListItem<'a>> FromSlice<'a> for RecordList<'a, T> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			data,
			records: s.parse_array16()?,
			data_type: core::marker::PhantomData,
		})
	}
}
impl<'a, T: RecordListItem<'a>> RecordList<'a, T> {
	/// Returns a number of items in the RecordList.
	pub fn len(&self) -> u16 {
		self.records.len()
	}

	/// Checks that RecordList is empty.
	pub fn is_empty(&self) -> bool {
		self.records.is_empty()
	}

	/// Returns RecordList value by index.
	pub fn get(&self, index: u16) -> Option<T> {
		let record = self.records.get(index)?;
		self.data
			.get(record.offset.to_usize()..)
			.and_then(|data| T::parse(record.tag, data))
	}

	/// Returns RecordList value by [`Tag`].
	pub fn find(&self, tag: Tag) -> Option<T> {
		let record = self.records.binary_search_by(|record| record.tag.cmp(&tag)).map(|p| p.1)?;
		self.data
			.get(record.offset.to_usize()..)
			.and_then(|data| T::parse(record.tag, data))
	}

	/// Returns RecordList value index by [`Tag`].
	pub fn index(&self, tag: Tag) -> Option<u16> {
		self.records.binary_search_by(|record| record.tag.cmp(&tag)).map(|p| p.0)
	}
}
impl<'a, T: RecordListItem<'a> + core::fmt::Debug> core::fmt::Debug for RecordList<'a, T> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_list().entries((0..self.len()).into_iter().map(|v| self.get(v))).finish()
	}
}
impl<'a, T: RecordListItem<'a>> IntoIterator for RecordList<'a, T> {
	type Item = T;
	type IntoIter = RecordListIter<'a, T>;

	#[inline]
	fn into_iter(self) -> Self::IntoIter {
		RecordListIter { list: self, index: 0 }
	}
}
/// An iterator over [`RecordList`] values.
#[allow(missing_debug_implementations)]
pub struct RecordListIter<'a, T: RecordListItem<'a>> {
	list: RecordList<'a, T>,
	index: u16,
}
impl<'a, T: RecordListItem<'a>> Iterator for RecordListIter<'a, T> {
	type Item = T;

	fn next(&mut self) -> Option<Self::Item> {
		if self.index < self.list.len() {
			self.index += 1;
			self.list.get(self.index - 1)
		} else {
			None
		}
	}
}

#[derive(Clone, Copy, Debug)]
struct TagRecord {
	tag: Tag,
	offset: Offset16,
}
impl FromData for TagRecord {
	const SIZE: usize = 6;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			tag: s.read::<Tag>()?,
			offset: s.read::<Offset16>()?,
		})
	}
}
